#pragma once

/**
 * @file unordered_map_me_order_book.h
 * @brief 基于无序映射的匹配引擎订单簿定义
 *
 * 本文件定义了使用std::unordered_map实现的匹配引擎订单簿类，
 * 它是交易系统中负责管理订单和执行撮合的核心组件的一个替代实现。
 * 与基本的MEOrderBook相比，该实现使用标准库的无序映射容器来存储订单和价格水平，
 * 可能在某些场景下提供不同的性能特性。
 *
 * @author 原作者
 * @date 未知
 */

#include <unordered_map>

#include "common/types.h"       // 基本类型定义
#include "common/mem_pool.h"    // 内存池，用于高效内存管理
#include "common/logging.h"     // 日志系统
#include "order_server/client_response.h" // 客户端响应结构
#include "market_data/market_update.h"    // 市场数据更新结构

#include "me_order.h"          // 匹配引擎订单定义

using namespace Common;

namespace Exchange {
  // 前向声明匹配引擎类
  class MatchingEngine;

  /**
   * @brief 基于无序映射的匹配引擎订单簿类
   * 
   * 订单簿是匹配引擎的核心组件，负责管理订单并执行撮合。
   * 该实现使用std::unordered_map存储订单和价格水平，实现了价格优先、时间优先的撮合算法。
   * 与基本的MEOrderBook相比，该实现可能在某些场景下提供不同的性能特性。
   */
  class UnorderedMapMEOrderBook final {
  public:
    /**
     * @brief 构造函数
     * 
     * 创建一个新的基于无序映射的订单簿实例，初始化内部数据结构和关联对象。
     * 
     * @param ticker_id 交易品种ID，用于标识该订单簿所管理的交易品种
     * @param logger 日志记录器，用于记录订单簿操作和状态
     * @param matching_engine 所属的匹配引擎实例，用于发送市场数据和客户端响应
     */
    explicit UnorderedMapMEOrderBook(TickerId ticker_id, Logger *logger, MatchingEngine *matching_engine);

    /**
     * @brief 析构函数
     * 
     * 销毁订单簿实例，记录最终状态并清理资源。
     */
    ~UnorderedMapMEOrderBook();

    /**
     * @brief 创建并添加新订单到订单簿
     * 
     * 根据提供的属性创建新订单并添加到订单簿中。
     * 会检查新订单是否与现有的对手方订单匹配，如果匹配则执行撮合。
     * 如果订单未完全成交，剩余部分将作为被动订单添加到订单簿中。
     * 
     * @param client_id 客户端ID，标识下单的客户
     * @param client_order_id 客户端订单ID，客户端用于标识订单的唯一标识符
     * @param ticker_id 交易品种ID，标识订单所交易的品种
     * @param side 交易方向（买/卖），标识订单是买单还是卖单
     * @param price 价格，订单的限价
     * @param qty 数量，订单的数量
     */
    auto add(ClientId client_id, OrderId client_order_id, TickerId ticker_id, Side side, Price price, Qty qty) noexcept -> void;

    /**
     * @brief 尝试取消订单簿中的订单
     * 
     * 根据客户端ID和订单ID查找并取消订单。
     * 如果订单不存在，会发出取消拒绝响应。
     * 成功取消的订单会从订单簿中移除，并发送相应的市场数据更新和客户端响应。
     * 
     * @param client_id 客户端ID，标识下单的客户
     * @param order_id 订单ID，客户端用于标识订单的唯一标识符
     * @param ticker_id 交易品种ID，标识订单所交易的品种
     */
    auto cancel(ClientId client_id, OrderId order_id, TickerId ticker_id) noexcept -> void;

    /**
     * @brief 将订单簿转换为字符串表示
     * 
     * 生成订单簿的文本表示，包括买卖盘的所有价格水平和订单。
     * 可用于日志记录、调试和状态监控。
     * 
     * @param detailed 是否包含详细信息，如果为true则包含每个订单的详细信息
     * @param validity_check 是否执行有效性检查，验证订单簿的内部一致性
     * @return 订单簿的字符串表示
     */
    auto toString(bool detailed, bool validity_check) const -> std::string;

    /**
     * @brief 删除默认构造函数、拷贝构造函数、移动构造函数和赋值运算符
     * 
     * 这确保订单簿对象不能被随意复制或移动，避免潜在的数据不一致问题。
     * 订单簿包含复杂的内部状态和指针结构，不适合复制或移动操作。
     */
    UnorderedMapMEOrderBook() = delete;
    UnorderedMapMEOrderBook(const UnorderedMapMEOrderBook &) = delete;
    UnorderedMapMEOrderBook(const UnorderedMapMEOrderBook &&) = delete;
    UnorderedMapMEOrderBook &operator=(const UnorderedMapMEOrderBook &) = delete;
    UnorderedMapMEOrderBook &operator=(const UnorderedMapMEOrderBook &&) = delete;

  private:
    /**
     * @brief 交易品种ID
     * 
     * 标识该订单簿所管理的交易品种
     */
    TickerId ticker_id_ = TickerId_INVALID;

    /**
     * @brief 父匹配引擎实例
     * 
     * 用于发布市场数据和客户端响应的匹配引擎指针
     */
    MatchingEngine *matching_engine_ = nullptr;

    /**
     * @brief 客户端订单哈希映射
     * 
     * 从ClientId -> OrderId -> MEOrder的无序映射
     * 用于快速查找特定客户端的特定订单
     */
    std::unordered_map<ClientId, std::unordered_map<OrderId, MEOrder *>> cid_oid_to_order_;

    /**
     * @brief 价格水平对象内存池
     * 
     * 管理MEOrdersAtPrice对象的内存池
     * 使用内存池可以减少动态内存分配，提高性能
     */
    MemPool<MEOrdersAtPrice> orders_at_price_pool_;

    /**
     * @brief 买盘最优价格指针
     * 
     * 指向买盘最优价格水平（最高买价）的指针
     * 这是买盘价格水平双向链表的头部
     */
    MEOrdersAtPrice *bids_by_price_ = nullptr;
    
    /**
     * @brief 卖盘最优价格指针
     * 
     * 指向卖盘最优价格水平（最低卖价）的指针
     * 这是卖盘价格水平双向链表的头部
     */
    MEOrdersAtPrice *asks_by_price_ = nullptr;

    /**
     * @brief 价格到订单集合的哈希映射
     * 
     * 从Price -> MEOrdersAtPrice的无序映射
     * 用于快速查找特定价格的订单集合
     */
    std::unordered_map<Price, MEOrdersAtPrice *> price_orders_at_price_;

    /**
     * @brief 订单对象内存池
     * 
     * 管理MEOrder对象的内存池
     * 使用内存池可以减少动态内存分配，提高性能
     */
    MemPool<MEOrder> order_pool_;

    /**
     * @brief 客户端响应对象
     * 
     * 用于构建和发送客户端响应的临时对象
     */
    MEClientResponse client_response_;
    
    /**
     * @brief 市场数据更新对象
     * 
     * 用于构建和发送市场数据更新的临时对象
     */
    MEMarketUpdate market_update_;

    /**
     * @brief 下一个市场订单ID
     * 
     * 用于生成唯一的市场内部订单ID
     * 每次分配新ID后递增
     */
    OrderId next_market_order_id_ = 1;

    /**
     * @brief 时间字符串
     * 
     * 用于日志记录的时间字符串缓存
     */
    std::string time_str_;
    
    /**
     * @brief 日志记录器
     * 
     * 用于记录订单簿操作和状态的日志对象
     */
    Logger *logger_ = nullptr;

  private:
    /**
     * @brief 生成新的市场订单ID
     * 
     * 每次调用时递增市场订单ID计数器，并返回新的ID值。
     * 这个方法用于确保每个订单在市场中都有唯一的标识符。
     * 
     * @return OrderId 新生成的市场订单ID
     */
    auto generateNewMarketOrderId() noexcept {
      return ++next_market_order_id_;
    }

    /**
     * @brief 将价格转换为哈希表索引
     * 
     * 使用价格对最大价格水平数(ME_MAX_PRICE_LEVELS)取模，将价格映射到固定大小的数组索引。
     * 这种哈希方法简单高效，但可能导致不同价格映射到相同的索引（哈希冲突）。
     * 在实际使用中，需要配合其他机制处理这种冲突。
     * 
     * @param price 要转换的价格值
     * @return 对应的哈希表数组索引，范围为[0, ME_MAX_PRICE_LEVELS-1]
     */
    auto priceToIndex(Price price) const noexcept {
      return (price % ME_MAX_PRICE_LEVELS);
    }

    /**
     * @brief 获取指定价格的订单集合
     * 
     * 根据价格查找并返回对应的MEOrdersAtPrice对象。
     * 该方法首先检查价格是否存在于无序映射中，如果不存在则返回nullptr。
     * 如果存在，则返回对应的订单集合指针。
     * 
     * @param price 要查找的价格值
     * @return 对应价格的订单集合指针，如果该价格没有订单则返回nullptr
     */
    auto getOrdersAtPrice(Price price) const noexcept -> MEOrdersAtPrice * {
      if(price_orders_at_price_.find(priceToIndex(price)) == price_orders_at_price_.end())
        return nullptr;

      return price_orders_at_price_.at(priceToIndex(price));
    }

    /**
     * @brief 添加新的价格水平到容器中
     * 
     * 将新的MEOrdersAtPrice对象添加到哈希表和价格水平的双向链表中。
     * 该方法会根据价格的高低和交易方向，保持价格水平链表的正确排序。
     * 对于买盘，价格水平按价格降序排列（高价优先）；
     * 对于卖盘，价格水平按价格升序排列（低价优先）。
     * 这种排序方式确保了价格优先原则的实现。
     * 
     * 如果是第一个价格水平，则直接设置为链表头部；
     * 否则，根据价格比较找到合适的插入位置，并更新相应的链表指针。
     * 
     * @param new_orders_at_price 要添加的新价格水平对象，包含价格、交易方向和订单链表
     */
    auto addOrdersAtPrice(MEOrdersAtPrice *new_orders_at_price) noexcept {
      // 将新价格水平添加到哈希表中
      price_orders_at_price_[priceToIndex(new_orders_at_price->price_)] = new_orders_at_price;

      // 获取对应交易方向的最优价格水平指针
      const auto best_orders_by_price = (new_orders_at_price->side_ == Side::BUY ? bids_by_price_ : asks_by_price_);
      if (UNLIKELY(!best_orders_by_price)) { // 如果该方向的价格链表为空
        // 将新价格水平设置为该方向的第一个价格水平
        (new_orders_at_price->side_ == Side::BUY ? bids_by_price_ : asks_by_price_) = new_orders_at_price;
        // 初始化双向链表，指向自身形成环
        new_orders_at_price->prev_entry_ = new_orders_at_price->next_entry_ = new_orders_at_price;
      } else { // 如果价格链表不为空，需要按价格顺序插入
        auto target = best_orders_by_price;
        // 判断是否应该在target之后添加
        // 卖单：如果新价格大于目标价格，则应该在其后添加（卖盘按价格升序排列）
        // 买单：如果新价格小于目标价格，则应该在其后添加（买盘按价格降序排列）
        bool add_after = ((new_orders_at_price->side_ == Side::SELL && new_orders_at_price->price_ > target->price_) ||
                          (new_orders_at_price->side_ == Side::BUY && new_orders_at_price->price_ < target->price_));
        if (add_after) { // 如果需要在target之后添加
          target = target->next_entry_; // 移动到下一个节点
          // 重新检查是否应该在新target之后添加
          add_after = ((new_orders_at_price->side_ == Side::SELL && new_orders_at_price->price_ > target->price_) ||
                       (new_orders_at_price->side_ == Side::BUY && new_orders_at_price->price_ < target->price_));
        }
        // 继续遍历链表，直到找到合适的插入位置
        while (add_after && target != best_orders_by_price) {
          add_after = ((new_orders_at_price->side_ == Side::SELL && new_orders_at_price->price_ > target->price_) ||
                       (new_orders_at_price->side_ == Side::BUY && new_orders_at_price->price_ < target->price_));
          if (add_after)
            target = target->next_entry_;
        }

        if (add_after) { // 在target之后添加新价格水平
          if (target == best_orders_by_price) {
            // 如果target是链表头，则实际上应该在链表尾部添加
            target = best_orders_by_price->prev_entry_;
          }
          // 执行双向链表的插入操作
          new_orders_at_price->prev_entry_ = target;
          target->next_entry_->prev_entry_ = new_orders_at_price;
          new_orders_at_price->next_entry_ = target->next_entry_;
          target->next_entry_ = new_orders_at_price;
        } else { // 在target之前添加新价格水平
          // 执行双向链表的插入操作
          new_orders_at_price->prev_entry_ = target->prev_entry_;
          new_orders_at_price->next_entry_ = target;
          target->prev_entry_->next_entry_ = new_orders_at_price;
          target->prev_entry_ = new_orders_at_price;

          // 检查是否需要更新最优价格指针
          // 买单：如果新价格大于当前最优价格，则成为新的最优价格（最高买价）
          // 卖单：如果新价格小于当前最优价格，则成为新的最优价格（最低卖价）
          if ((new_orders_at_price->side_ == Side::BUY && new_orders_at_price->price_ > best_orders_by_price->price_) ||
              (new_orders_at_price->side_ == Side::SELL && new_orders_at_price->price_ < best_orders_by_price->price_)) {
            target->next_entry_ = (target->next_entry_ == best_orders_by_price ? new_orders_at_price : target->next_entry_);
            (new_orders_at_price->side_ == Side::BUY ? bids_by_price_ : asks_by_price_) = new_orders_at_price;
          }
        }
      }
    }

    /**
     * @brief 从容器中移除价格水平
     * 
     * 从哈希表和价格水平的双向链表中移除指定的价格水平。
     * 该方法在价格水平上的所有订单被移除后调用，用于清理空的价格水平。
     * 
     * 如果要移除的是唯一的价格水平，则将相应交易方向的链表头部设置为nullptr；
     * 否则，从双向链表中移除该节点，并更新相邻节点的指针。
     * 如果移除的是最优价格节点，还需要更新最优价格指针。
     * 
     * 最后，从哈希表中移除该价格水平，并释放内存池中的对象。
     * 
     * @param side 交易方向，指定要移除的是买盘还是卖盘的价格水平
     * @param price 要移除的价格值
     */
    auto removeOrdersAtPrice(Side side, Price price) noexcept {
      // 获取对应交易方向的最优价格水平指针
      const auto best_orders_by_price = (side == Side::BUY ? bids_by_price_ : asks_by_price_);
      // 获取要移除的价格水平
      auto orders_at_price = getOrdersAtPrice(price);

      if (UNLIKELY(orders_at_price->next_entry_ == orders_at_price)) { // 如果该价格水平是链表中唯一的节点
        // 将该方向的价格链表置为空
        (side == Side::BUY ? bids_by_price_ : asks_by_price_) = nullptr;
      } else { // 如果链表中有多个节点
        // 从双向链表中移除该节点
        orders_at_price->prev_entry_->next_entry_ = orders_at_price->next_entry_;
        orders_at_price->next_entry_->prev_entry_ = orders_at_price->prev_entry_;

        // 如果移除的是最优价格节点，需要更新最优价格指针
        if (orders_at_price == best_orders_by_price) {
          (side == Side::BUY ? bids_by_price_ : asks_by_price_) = orders_at_price->next_entry_;
        }

        // 清除节点的链接
        orders_at_price->prev_entry_ = orders_at_price->next_entry_ = nullptr;
      }

      // 从哈希表中移除该价格水平
      price_orders_at_price_[priceToIndex(price)] = nullptr;

      // 释放内存池中的对象
      orders_at_price_pool_.deallocate(orders_at_price);
    }

    /**
     * @brief 获取指定价格的下一个优先级
     * 
     * 计算并返回指定价格水平上的下一个可用的优先级值。
     * 如果该价格水平不存在，则返回1作为初始优先级。
     * 否则，返回该价格水平上最后一个订单的优先级加1。
     * 这确保了同一价格水平上的订单按照到达时间顺序排列。
     * 
     * @param price 要查询的价格值
     * @return 该价格水平的下一个可用优先级值
     */
    auto getNextPriority(Price price) noexcept {
      // 获取指定价格的订单集合
      const auto orders_at_price = getOrdersAtPrice(price);
      // 如果该价格水平不存在，返回初始优先级值1
      if (!orders_at_price)
        return 1lu;

      // 返回该价格水平上最后一个订单的优先级加1
      return orders_at_price->first_me_order_->prev_order_->priority_ + 1;
    }

    /**
     * @brief 执行订单撤合
     * 
     * 将一个新的主动订单与已存在的被动订单进行撤合，并生成相应的客户端响应和市场数据更新。
     * 根据撤合结果更新被动订单（bid_itr）的状态，如果完全成交则将其从订单簿中移除。
     * 主动订单的剩余数量将通过leaves_qty参数返回。
     * 
     * @param ticker_id 交易品种ID
     * @param client_id 客户端ID，主动订单的客户标识
     * @param side 交易方向，主动订单的买卖方向
     * @param client_order_id 客户端订单ID，主动订单的客户端标识
     * @param new_market_order_id 新的市场订单ID，主动订单的内部标识
     * @param bid_itr 被动订单指针，指向要与主动订单撤合的被动订单
     * @param leaves_qty 输出参数，用于返回主动订单的剩余数量
     */
    auto match(TickerId ticker_id, ClientId client_id, Side side, OrderId client_order_id, OrderId new_market_order_id, MEOrder* bid_itr, Qty* leaves_qty) noexcept;

    /**
     * @brief 检查新订单是否可与已存在的被动订单撤合
     * 
     * 检查具有指定属性的新订单是否可以与订单簿中对手方的被动订单进行撤合。
     * 如果可以撤合，则调用match()方法执行撤合操作，并返回新订单的剩余数量。
     * 这个方法是实现价格-时间优先撤合算法的关键部分。
     * 
     * @param client_id 客户端ID，新订单的客户标识
     * @param client_order_id 客户端订单ID，新订单的客户端标识
     * @param ticker_id 交易品种ID
     * @param side 交易方向，新订单的买卖方向
     * @param price 价格，新订单的限价
     * @param qty 数量，新订单的数量
     * @param new_market_order_id 新的市场订单ID，新订单的内部标识
     * @return 新订单的剩余数量，如果完全成交则为0
     */
    auto checkForMatch(ClientId client_id, OrderId client_order_id, TickerId ticker_id, Side side, Price price, Qty qty, Qty new_market_order_id) noexcept;

    /**
     * @brief 从容器中移除并释放订单
     * 
     * 从订单簿中移除指定的订单，并释放其占用的内存。
     * 该方法处理两种情况：
     * 1. 如果该订单是其价格水平上的唯一订单，则移除整个价格水平。
     * 2. 如果该价格水平上有多个订单，则从订单链表中移除该订单并更新相关指针。
     * 
     * 最后，从客户端订单映射中移除该订单，并释放内存池中的对象。
     * 
     * @param order 要移除的订单指针
     */
    auto removeOrder(MEOrder *order) noexcept {
      // 获取订单所在的价格水平
      auto orders_at_price = getOrdersAtPrice(order->price_);

      if (order->prev_order_ == order) { // 如果该订单是价格水平上的唯一订单
        // 移除整个价格水平
        removeOrdersAtPrice(order->side_, order->price_);
      } else { // 从链表中移除该订单
        // 获取订单的前后节点
        const auto order_before = order->prev_order_;
        const auto order_after = order->next_order_;
        // 更新前后节点的链接
        order_before->next_order_ = order_after;
        order_after->prev_order_ = order_before;

        // 如果要移除的是价格水平的第一个订单，需要更新价格水平的第一个订单指针
        if (orders_at_price->first_me_order_ == order) {
          orders_at_price->first_me_order_ = order_after;
        }

        // 清除订单的链接
        order->prev_order_ = order->next_order_ = nullptr;
      }

      // 从客户端订单映射中移除该订单
      cid_oid_to_order_[order->client_id_][order->client_order_id_] = nullptr;
      // 释放订单对象的内存
      order_pool_.deallocate(order);
    }

    /**
     * @brief 将单个订单添加到其所属价格水平的FIFO队列尾部
     * 
     * 将新订单添加到订单簿中其对应价格水平的队列尾部。
     * 该方法处理两种情况：
     * 1. 如果该价格水平不存在，则创建新的价格水平并将订单添加到其中。
     * 2. 如果该价格水平已存在，则将订单添加到现有订单链表的尾部。
     * 
     * 最后，将订单添加到客户端订单映射中，以便快速查找。
     * 
     * @param order 要添加的订单指针
     */
    auto addOrder(MEOrder *order) noexcept {
      // 获取订单所属的价格水平
      const auto orders_at_price = getOrdersAtPrice(order->price_);

      if (!orders_at_price) { // 如果该价格水平不存在
        // 初始化订单的前后指针，形成单节点循环链表
        order->next_order_ = order->prev_order_ = order;

        // 从内存池中分配新的价格水平对象
        auto new_orders_at_price = orders_at_price_pool_.allocate(order->side_, order->price_, order, nullptr, nullptr);
        // 将新的价格水平添加到容器中
        addOrdersAtPrice(new_orders_at_price);
      } else { // 如果该价格水平已存在
        // 获取该价格水平的第一个订单
        auto first_order = (orders_at_price ? orders_at_price->first_me_order_ : nullptr);

        // 将新订单添加到现有订单链表的尾部
        first_order->prev_order_->next_order_ = order;
        order->prev_order_ = first_order->prev_order_;
        order->next_order_ = first_order;
        first_order->prev_order_ = order;
      }

      // 将订单添加到客户端订单映射中，以便快速查找
      cid_oid_to_order_[order->client_id_][order->client_order_id_] = order;
    }
  };
}
